package com.smedi.ismedi.personnel.core.domain.aggregatemodel.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.smedi.ismedi.organization.api.dto.response.HrOrganizationResponse;
import com.smedi.ismedi.personnel.core.application.command.OrganizationAssignmentListQueryCommand;
import com.smedi.ismedi.personnel.core.application.command.PersonListQueryCommand;
import com.smedi.ismedi.personnel.core.application.command.PersonOneQueryCommand;
import com.smedi.ismedi.personnel.core.application.convert.AppConverter;
import com.smedi.ismedi.personnel.core.application.query.PersonQueryService;
import com.smedi.ismedi.personnel.core.application.query.vo.InternalAssignmentVo;
import com.smedi.ismedi.personnel.core.application.query.vo.InternalPersonAssignmentVo;
import com.smedi.ismedi.personnel.core.application.query.vo.PersonDetailVo;
import com.smedi.ismedi.personnel.core.application.query.vo.PersonInfoVo;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.Assignment;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.Person;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.ProfileImage;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.Signature;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.model.OrganizationAssignment;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.model.SimplePerson;
import com.smedi.ismedi.personnel.core.domain.repository.AssignmentRepository;
import com.smedi.ismedi.personnel.core.domain.repository.PersonRepository;
import com.smedi.ismedi.personnel.core.domain.repository.ProfileImageRepository;
import com.smedi.ismedi.personnel.core.domain.repository.SignatureRepository;
import com.smedi.ismedi.personnel.core.infrastructure.constant.CustomError;
import com.smedi.ismedi.personnel.core.infrastructure.enumerate.GenderEnum;
import com.smedi.ismedi.personnel.core.infrastructure.enumerate.PersonStatusEnum;
import com.smedi.ismedi.personnel.core.infrastructure.enumerate.PersonTypeEnum;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.dto.OrganizationAssignmentQuery;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.dto.PersonQuery;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.model.AssignmentEntity;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.mybatis.mapper.AssignmentMapper;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.mybatis.mapper.InternalAssignmentMapper;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.mybatis.mapper.PersonMapper;
import com.smedi.ismedi.personnel.core.port.adapter.service.AuthorityAccessRemoteApiService;
import com.smedi.ismedi.personnel.core.port.adapter.service.OrganizationRemoteApiService;
import com.smedi.ismedi.web.starter.exception.LogicException;
import com.smedi.ismedi.web.starter.util.DozerUtil;
import com.smedi.ismedi.web.starter.util.SegmentateUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: xulinjun
 * @Date: 2021-6-11 9:24
 */
@Slf4j
@Service
@AllArgsConstructor
public class PersonQueryServiceImpl implements PersonQueryService {
    private final InternalAssignmentMapper internalAssignmentMapper;
    private final OrganizationRemoteApiService organizationRemoteApiService;
    private final AssignmentRepository assignmentRepository;
    private final PersonRepository personRepository;
    private final ProfileImageRepository profileImageRepository;
    private final SignatureRepository signatureRepository;
    private final PersonMapper personMapper;
    private final AuthorityAccessRemoteApiService authorityAccessRemoteApiService;
    private final AssignmentMapper assignmentMapper;
    private final SegmentateUtil segmentateUtil;
    private final DozerUtil dozerUtil;

    private final AppConverter appConverter;

    @Override
    public List<PersonInfoVo> getPersonsByCurrentHrOrganizationId(Long hrOrganizationId) {
        List<Long> personIds = assignmentRepository.selectByHrOrganizationId(hrOrganizationId).stream().map(Assignment::getPersonId).collect(Collectors.toList());
        List<Person> list = new ArrayList<>();
        personIds.stream().forEach(personId -> {
            Person person = personRepository.selectByPersonId(personId,false);
            if (person != null) {
                list.add(person);
            }
        });

        return dozerUtil.convertor(list, PersonInfoVo.class);
    }

    @Override
    public IPage getPersonPaginationByInternalAssignment(Long offset, Long pageSize, Long hrOrganizationId, String lastName, String userName, String employeeNum) {
        Page<InternalPersonAssignmentVo> page = new Page<>(offset, pageSize);
        //page.addOrder(new OrderItem().desc("person_id"));
        page.addOrder(new OrderItem().asc("person_order")).addOrder(new OrderItem().asc("created_date"));
        IPage<InternalPersonAssignmentVo> pagination = internalAssignmentMapper.selectInternalAssignmentVoPagination(page, hrOrganizationId, lastName, userName, employeeNum);
        pagination.setRecords(pagination.getRecords().stream().sorted(Comparator.comparing(InternalPersonAssignmentVo::getPersonOrder, Comparator.nullsLast(Integer::compareTo))).collect(Collectors.toList()));
        pagination.getRecords().stream().forEach(internalPersonAssignmentVo -> {
            internalPersonAssignmentVo.setGenderMeaning(GenderEnum.getName(internalPersonAssignmentVo.getGender()))
                    .setPersonTypeMeaning(PersonTypeEnum.getName(internalPersonAssignmentVo.getPersonType()))
                    .setStatusMeaning(PersonStatusEnum.getName(internalPersonAssignmentVo.getStatus()))
                    .setRoleNames(authorityAccessRemoteApiService.queryRoleNameByPersonId(internalPersonAssignmentVo.getPersonId()));
            Assignment assignment = assignmentRepository.selectByPersonIdAndHrOrganizationId(internalPersonAssignmentVo.getPersonId(), hrOrganizationId);
            if (assignment != null) {
                internalPersonAssignmentVo.setInternalJobNames(assignment.getHrJob());
            }
        });

        return pagination;
    }

    @Override
    public IPage getPersonPaginationByInternalAssignmentContainsChildren(Long offset, Long pageSize, Long hrOrganizationId, String lastName, String userName, String employeeNum) {
        // 获取该组织下的所有子组织
        List<Long> orgChildrenOrgIds = organizationRemoteApiService.getHrOrganizationChildrenOrgIdList(hrOrganizationId);

        Page<InternalPersonAssignmentVo> page = new Page<>(offset, pageSize);
        page.addOrder(new OrderItem().asc("person_order")).addOrder(new OrderItem().asc("created_date"));
        //page.addOrder(new OrderItem().desc("person_id"));
        List<List<Long>> seg = segmentateUtil.doCut(orgChildrenOrgIds, 999);
        IPage<InternalPersonAssignmentVo> pagination = internalAssignmentMapper.selectInternalAssignmentVoContainsHrOrganizationChildrenPagination(page, seg, lastName, userName, employeeNum);
        pagination.setRecords(pagination.getRecords().stream().sorted(Comparator.comparing(InternalPersonAssignmentVo::getPersonOrder, Comparator.nullsLast(Integer::compareTo))).collect(Collectors.toList()));
        pagination.getRecords().stream().forEach(internalPersonAssignmentVo -> {
            internalPersonAssignmentVo.setGenderMeaning(GenderEnum.getName(internalPersonAssignmentVo.getGender()))
                    .setPersonTypeMeaning(PersonTypeEnum.getName(internalPersonAssignmentVo.getPersonType()))
                    .setStatusMeaning(PersonStatusEnum.getName(internalPersonAssignmentVo.getStatus()))
                    .setRoleNames(authorityAccessRemoteApiService.queryRoleNameByPersonId(internalPersonAssignmentVo.getPersonId()));
            // 获取这个人在这些组织下的Assignment信息
            String hrJobs = assignmentRepository.selectPyPersonIdAndHrOrganizationIds(internalPersonAssignmentVo.getPersonId(), orgChildrenOrgIds);
            internalPersonAssignmentVo.setInternalJobNames(hrJobs);
        });

        return pagination;
    }

    @Override
    public IPage getPersonPaginationPickUp(Long offset, Long pageSize, String lastName, String employeeNum) {
        return personRepository.selectPersonPaginationByLastNameAndEmployeeNum(offset, pageSize, lastName, employeeNum,false);
    }

    // 获取详细的分配信息
    private InternalAssignmentVo getAssignmentDetailInfoVo(Assignment assignment) {
        // 获取组织信息
        HrOrganizationResponse hrOrganizationResponse = organizationRemoteApiService.getHrOrganizationView(assignment.getHrOrganizationId());
        if (hrOrganizationResponse != null) {
            InternalAssignmentVo internalAssignmentVo = dozerUtil.convertor(assignment, InternalAssignmentVo.class);
            internalAssignmentVo.setHrOrganizationCode(hrOrganizationResponse.getOrganizationCode())
                    .setHrOrganizationName(hrOrganizationResponse.getFullName())
                    .setHrOrganizationShortName(hrOrganizationResponse.getShortName())
                    .setHrOrganizationType(hrOrganizationResponse.getType())
                    .setHrOrganizationTypeMeaning(hrOrganizationResponse.getTypeMeaning());

            List<String> hrOrganizationType = new ArrayList<>();
            hrOrganizationType.add("COMPANY_MANAGEMENT_ORGANIZATION");
            hrOrganizationType.add("COMPANY");
            hrOrganizationType.add("PROJECT_MANAGEMENT_ORGANIZATION");

            while (!hrOrganizationType.contains(hrOrganizationResponse.getType())) {
                if (hrOrganizationResponse.getParentId() != null) {
                    hrOrganizationResponse = organizationRemoteApiService.getHrOrganizationView(hrOrganizationResponse.getParentId());
                }
            }
            internalAssignmentVo.setSubHrOrganizationId(hrOrganizationResponse.getHrOrganizationId())
                    .setSubHrOrganizationName(hrOrganizationResponse.getFullName());

            return internalAssignmentVo;
        } else {
            return null;
        }
    }

    @Override
    public PersonInfoVo getPersonInfo(Long personId, Long hrOrganizationId) {
        Person person = personRepository.selectByPersonId(personId,null);
        if (person != null) {
            PersonInfoVo personInfoVo = dozerUtil.convertor(person, PersonInfoVo.class);
            personInfoVo.setGenderMeaning(GenderEnum.getName(personInfoVo.getGender()))
                    .setPersonTypeMeaning(PersonTypeEnum.getName(personInfoVo.getPersonType()))
                    .setStatusMeaning(PersonTypeEnum.getName(personInfoVo.getStatus()));

            // 如果当前传入了组织id，则抓取这个组织的分配信息。反之则把所有分配信息都抓取出来
            if (hrOrganizationId != null) {
                Assignment assignment = assignmentRepository.selectByPersonIdAndHrOrganizationId(personId, hrOrganizationId);
                InternalAssignmentVo internalAssignmentVo = getAssignmentDetailInfoVo(assignment);
                if (internalAssignmentVo != null) {
                    personInfoVo.getInternalAssignments().add(internalAssignmentVo); // 添加数据
                }
            } else {
                List<Assignment> assignments = assignmentRepository.selectByPersonId(personId);
                for (Assignment assignment : assignments) {
                    InternalAssignmentVo internalAssignmentVo = getAssignmentDetailInfoVo(assignment);
                    if (internalAssignmentVo != null) {
                        personInfoVo.getInternalAssignments().add(internalAssignmentVo); // 添加数据
                    }
                }
            }

            getProfileImageAndSignature(personInfoVo, personId);

            return personInfoVo;
        } else {
            throw LogicException.raise(CustomError.PERSON_NOT_EXIST);
        }
    }

    @Override
    public List<Long> getPersonIdsByLastNameAndUserNameAndEmployeeNumAndMobile(String lastName, String userName, String employeeNum, String mobile) {
        return personMapper.selectPersonIdByLastNameAndUserNameAndEmployeeNumAndMobile(lastName, userName, employeeNum, mobile);
    }

    @Override
    public List<PersonInfoVo> getPersonInfoByPersonIds(List<Long> personIds) {
        List<PersonInfoVo> personInfoVos = new ArrayList<>();
        for (Long personId : personIds) {
            Person person = personRepository.selectByPersonId(personId,null);

            PersonInfoVo personInfoVo = dozerUtil.convertor(person, PersonInfoVo.class);
            if (personInfoVo != null) {
                personInfoVo.setPersonTypeMeaning(PersonTypeEnum.getName(person.getPersonType()))
                        .setStatusMeaning(PersonStatusEnum.getName(person.getStatus()));
                personInfoVos.add(personInfoVo);
            }
        }
        return personInfoVos;
    }

    @Override
    public List<Person> getPeopleByPersonIds(List<Long> personIds) {
        List<Person> people = personRepository.selectByPersonIds(personIds,null);
        List<ProfileImage> profileImages = profileImageRepository.selectProfileImageByPersonIds(personIds);
//        List<Assignment> assignments = assignmentRepository.selectByPersonIdsSkipSql(personIds);
        if (!profileImages.isEmpty()) {
            Map<Long, List<ProfileImage>> imagePersonIdMap = profileImages.stream().collect(Collectors.groupingBy(ProfileImage::getPersonId));
            for (Person person : people) {
                ProfileImage profileImage = imagePersonIdMap.get(person.getPersonId()).get(0);
                person.setProfileImage(profileImage);
            }
        }
//        if (!assignments.isEmpty()) {
//            Map<Long, List<Assignment>> assignmentPersonIdMap = assignments.stream().collect(Collectors.groupingBy(Assignment::getPersonId));
//            for (Person person : people) {
//                List<Assignment> assignmentsByPersonId = assignmentPersonIdMap.get(person.getPersonId());
//                person.setInternalAssignments(assignmentsByPersonId);
//            }
//        }
        return people;
    }

    @Override
    public IPage getPersonManagementPagination(Long offset, Long pageSize, String lastName, String userName, String employeeNum, String personType, String status, Boolean internal, Long nonOrganizationId, boolean nonAllHrOrganization) {
        Page<PersonInfoVo> page = new Page<>(offset, pageSize);
        page.addOrder(OrderItem.desc("id"));
        List<Long> nonPersonIds = new ArrayList<>();
        if (nonOrganizationId != null) {
            List<Assignment> assignments = assignmentRepository.selectByHrOrganizationId(nonOrganizationId);
            nonPersonIds = assignments.stream().map(Assignment::getPersonId).collect(Collectors.toList());
        }
        if (nonAllHrOrganization) {
            List<AssignmentEntity> assignmentEntities = assignmentMapper.selectList(null);
            nonPersonIds = assignmentEntities.stream().map(AssignmentEntity::getPersonId).distinct().collect(Collectors.toList());
        }

        IPage<PersonInfoVo> pagination = personMapper.selectPersonManagementPagination(page, lastName, userName, employeeNum, personType, status, internal, nonPersonIds);
        pagination.getRecords().forEach(personInfoVo -> personInfoVo.setPersonTypeMeaning(PersonTypeEnum.getName(personInfoVo.getPersonType()))
                .setStatusMeaning(PersonStatusEnum.getName(personInfoVo.getStatus())));

        return pagination;
    }

    @Override
    public PersonInfoVo getPersonDetailInfo(Long personId) {
        Person person = personRepository.selectByPersonId(personId,null);
        if (person != null) {
            PersonInfoVo personInfoVo = dozerUtil.convertor(person, PersonInfoVo.class);

            personInfoVo.setGenderMeaning(GenderEnum.getName(personInfoVo.getGender()))
                    .setPersonTypeMeaning(PersonTypeEnum.getName(personInfoVo.getPersonType()))
                    .setStatusMeaning(PersonTypeEnum.getName(personInfoVo.getStatus()));

            getProfileImageAndSignature(personInfoVo, personId);

            // 获取内部分配信息
            List<Assignment> internalAssignments = assignmentRepository.selectByPersonId(personId);
            for (Assignment assignment : internalAssignments) {
                personInfoVo.getInternalAssignments().add(getAssignmentDetailInfoVo(assignment));
            }

            return personInfoVo;
        } else {
            throw LogicException.raise(CustomError.PERSON_NOT_EXIST);
        }
    }

    @Override
    public PersonInfoVo getPersonInfo(Long personId) {
        Person person = personRepository.selectByPersonId(personId,null);
        if (person != null) {
            PersonInfoVo personInfoVo = dozerUtil.convertor(person, PersonInfoVo.class);
            personInfoVo.setGenderMeaning(GenderEnum.getName(personInfoVo.getGender()))
                    .setPersonTypeMeaning(PersonTypeEnum.getName(personInfoVo.getPersonType()))
                    .setStatusMeaning(PersonTypeEnum.getName(personInfoVo.getStatus()));

            getProfileImageAndSignature(personInfoVo, personId);

            return personInfoVo;
        } else {
            throw LogicException.raise(CustomError.PERSON_NOT_EXIST);
        }
    }

    @Override
    public List<Long> getAssignmentHrOrganizationIdsByPersonId(Long personId) {
        return assignmentRepository.selectByPersonIdSkipSql(personId).stream().map(Assignment::getHrOrganizationId).distinct().collect(Collectors.toList());
    }

    @Override
    public List<Assignment> getInternalAssignmentByPersonId(Long personId) {
        return assignmentRepository.selectByPersonIdSkipSql(personId);
    }

    @Override
    public List<Long> getPersonIdsByOrganizationId(Long hrOrganizationId) {
        List<Assignment> assignments = assignmentRepository.selectByHrOrganizationIdSkipSql(hrOrganizationId);
        return assignments.stream().map(Assignment::getPersonId).collect(Collectors.toList());
    }

    @Override
    public List<SimplePerson> getSimplePersonList(PersonListQueryCommand personListQueryCommand) {
        PersonQuery personQuery = appConverter.personListQueryCommandToPersonQuery(personListQueryCommand);
        return personRepository.getSimplePersonList(personQuery);
    }


    @Override
    public List<PersonDetailVo> getPersonDetailList(PersonListQueryCommand personListQueryCommand) {
        PersonQuery personQuery = appConverter.personListQueryCommandToPersonQuery(personListQueryCommand);
        return personRepository.getPersonDetailList(personQuery);
    }

    @Override
    public List<OrganizationAssignment> getOrganizationAssignmentList(OrganizationAssignmentListQueryCommand organizationAssignmentListQueryCommand) {
        OrganizationAssignmentQuery organizationAssignmentQuery = appConverter.organizationAssignmentListQueryCommandToOrganizationAssignmentQuery(organizationAssignmentListQueryCommand);
        return personRepository.getOrganizationAssignmentList(organizationAssignmentQuery);
    }

    @Override
    public SimplePerson getSimplePersonOne(PersonOneQueryCommand personOneQueryCommand) {
        PersonQuery personQuery = appConverter.personOneQueryCommandToPersonQuery(personOneQueryCommand);
        return personRepository.getSimplePersonOne(personQuery);
    }

    /**
     * 获取头像和签章
     *
     * @param personInfoVo
     * @param personId
     */
    private void getProfileImageAndSignature(PersonInfoVo personInfoVo, Long personId) {
        // 获取人员照片头像数据
        ProfileImage profileImage = profileImageRepository.selectProfileImageByPersonId(personId);
        if (profileImage != null) {
            personInfoVo.setImageUUID(profileImage.getUuid());
        }

        // 获取人员签章数据
        Signature signature = signatureRepository.selectSignatureByPersonId(personId);
        if (signature != null) {
            personInfoVo.setSignatureUUID(signature.getUuid());
        }
    }
}
